// FastBitArrayCpp.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "FastBitArrayCpp.h"

#define UNROLL
#define SSE

#ifdef SSE
void __stdcall SSEAnd(uint64_t* lhs, uint64_t* rhs, int count)
{
    auto diffLhs = ((uintptr_t)lhs & 0xF);
    auto diffRhs = ((uintptr_t)rhs   & 0xF);

    int i=0;
    if (diffLhs == diffRhs && (diffLhs == 0 || diffLhs == 8))
    {
        if (diffLhs == 8 && count > 0)
        {
            lhs[i] &= rhs[i];
            ++i; 
        }
        for (; i < count-2; i += 2)
        {
            auto l = reinterpret_cast<__m128i*>(lhs+i);
            auto r = reinterpret_cast<__m128i*>(rhs+i);

            auto lhs0 = _mm_load_si128(l+0);
            auto rhs0 = _mm_load_si128(r+0);
            auto result0 = _mm_and_si128(lhs0, rhs0);
            _mm_store_si128(l+0, result0);
        }
    }
    else
    {
        if (diffLhs == 0 || diffLhs == 8)
        {
            if (diffLhs == 8 && count > 0)
            {
                lhs[i] &= rhs[i];
                ++i; 
            }

            for (; i < count-2; i += 2)
            {
                auto l = reinterpret_cast<__m128i*>(lhs+i);
                auto r = reinterpret_cast<__m128i*>(rhs+i);

                auto lhs0 = _mm_load_si128(l+0);
                auto rhs0 = _mm_loadu_si128(r+0);
                auto result0 = _mm_and_si128(lhs0, rhs0);
                _mm_store_si128(l+0, result0);
            }
        }
        else
        {
            for (; i < count-2; i += 2)
            {
                auto l = reinterpret_cast<__m128i*>(lhs+i);
                auto r = reinterpret_cast<__m128i*>(rhs+i);

                auto lhs0 = _mm_loadu_si128(l+0);
                auto rhs0 = _mm_loadu_si128(r+0);
                auto result0 = _mm_and_si128(lhs0, rhs0);
                _mm_storeu_si128(l+0, result0);
            }
        }
    }

    for (; i < count; ++i)
        lhs[i] &= rhs[i];
}

void __stdcall SSEOr(uint64_t* lhs, uint64_t* rhs, int count)
{
    auto diffLhs = ((uintptr_t)lhs & 0xF);
    auto diffRhs = ((uintptr_t)rhs   & 0xF);

    int i=0;
    if (diffLhs == diffRhs && (diffLhs == 0 || diffLhs == 8))
    {
        if (diffLhs == 8 && count > 0)
        {
            lhs[i] |= rhs[i];
            ++i;
        }
        for (; i < count-2; i += 2)
        {
            auto l = reinterpret_cast<__m128i*>(lhs+i);
            auto r = reinterpret_cast<__m128i*>(rhs+i);

            auto lhs0 = _mm_load_si128(l+0);
            auto rhs0 = _mm_load_si128(r+0);
            auto result0 = _mm_or_si128(lhs0, rhs0);
            _mm_store_si128(l+0, result0);
        }
    }
    else
    {
        if (diffLhs == 0 || diffLhs == 8)
        {
            if (diffLhs == 8 && count > 0)
            {
                lhs[i] |= rhs[i];
                ++i;
            }
            for (; i < count-2; i += 2)
            {
                auto l = reinterpret_cast<__m128i*>(lhs+i);
                auto r = reinterpret_cast<__m128i*>(rhs+i);

                auto lhs0 = _mm_load_si128(l+0);
                auto rhs0 = _mm_loadu_si128(r+0);
                auto result0 = _mm_or_si128(lhs0, rhs0);
                _mm_store_si128(l+0, result0);
            }
        }
        else
        {
            for (; i < count-2; i += 2)
            {
                auto l = reinterpret_cast<__m128i*>(lhs+i);
                auto r = reinterpret_cast<__m128i*>(rhs+i);

                auto lhs0 = _mm_loadu_si128(l+0);
                auto rhs0 = _mm_loadu_si128(r+0);
                auto result0 = _mm_or_si128(lhs0, rhs0);
                _mm_storeu_si128(l+0, result0);
            }
        }
    }

    for (; i < count; ++i)
        lhs[i] |= rhs[i];
}

void __stdcall SSEXor(uint64_t* lhs, uint64_t* rhs, int count)
{
    auto diffLhs = ((uintptr_t)lhs & 0xF);
    auto diffRhs = ((uintptr_t)rhs   & 0xF);

    int i=0;
    if (diffLhs == diffRhs && (diffLhs == 0 || diffLhs == 8))
    {
        if (diffLhs == 8 && count > 0)
        {
            lhs[i] ^= rhs[i];
            ++i;
        }
        for (; i < count-2; i += 2)
        {
            auto l = reinterpret_cast<__m128i*>(lhs+i);
            auto r = reinterpret_cast<__m128i*>(rhs+i);

            auto lhs0 = _mm_load_si128(l+0);
            auto rhs0 = _mm_load_si128(r+0);
            auto result0 = _mm_xor_si128(lhs0, rhs0);
            _mm_store_si128(l+0, result0);
        }
    }
    else
    {
        if (diffLhs == 0 || diffLhs == 8)
        {
            if (diffLhs == 8 && count > 0)
            {
                lhs[i] ^= rhs[i];
                ++i;
            }
            for (; i < count-2; i += 2)
            {
                auto l = reinterpret_cast<__m128i*>(lhs+i);
                auto r = reinterpret_cast<__m128i*>(rhs+i);

                auto lhs0 = _mm_load_si128(l+0);
                auto rhs0 = _mm_loadu_si128(r+0);
                auto result0 = _mm_xor_si128(lhs0, rhs0);
                _mm_store_si128(l+0, result0);
            }
        }
        else
        {
            for (; i < count-2; i += 2)
            {
                auto l = reinterpret_cast<__m128i*>(lhs+i);
                auto r = reinterpret_cast<__m128i*>(rhs+i);

                auto lhs0 = _mm_loadu_si128(l+0);
                auto rhs0 = _mm_loadu_si128(r+0);
                auto result0 = _mm_xor_si128(lhs0, rhs0);
                _mm_storeu_si128(l+0, result0);
            }
        }
    }

    for (; i < count; ++i)
        lhs[i] ^= rhs[i];
}

void __stdcall SSENot(uint64_t* lhs, int count)
{
    auto alltrue = _mm_set1_epi32(0xFFFFFFFF);
    auto diffLhs = ((uintptr_t)lhs & 0xF);
  
    int i=0;
    if (diffLhs == 0 || diffLhs == 8)
    {
        if (diffLhs == 8 && count > 0)
        {
            lhs[i] = ~lhs[i];
            ++i;
        }
        for (; i < count-4; i += 4)
        {
            auto l = reinterpret_cast<__m128i*>(lhs+i);

            auto lhs0 = _mm_load_si128(l+0);
            auto lhs1 = _mm_load_si128(l+1);

            auto result0 = _mm_andnot_si128(lhs0, alltrue);
            auto result1 = _mm_andnot_si128(lhs1, alltrue);

            _mm_store_si128(l+0, result0);
            _mm_store_si128(l+1, result1);
        }
    }
    else
    {
        for (; i < count-4; i += 4)
        {
            auto l = reinterpret_cast<__m128i*>(lhs+i);

            auto lhs0 = _mm_loadu_si128(l+0);
            auto lhs1 = _mm_loadu_si128(l+1);

            auto result0 = _mm_andnot_si128(lhs0, alltrue);
            auto result1 = _mm_andnot_si128(lhs1, alltrue);

            _mm_storeu_si128(l+0, result0);
            _mm_storeu_si128(l+1, result1);
        }
    }

    for (; i < count; ++i)
        lhs[i] = ~lhs[i];
}

// -----------------------------------------------------------------------------------

#else

void __stdcall SSEAnd(uint64_t* lhs, uint64_t* rhs, int count)
{
    int i=0;

#ifdef UNROLL
    for (; i < count-4; i += 4)
    {
        lhs[i+0] &= rhs[i+0];
        lhs[i+1] &= rhs[i+1];
        lhs[i+2] &= rhs[i+2];
        lhs[i+3] &= rhs[i+3];
    }
#endif

    for (; i < count; ++i)
        lhs[i] &= rhs[i];
}

void __stdcall SSEOr(uint64_t* lhs, uint64_t* rhs, int count)
{
    int i=0;

#ifdef UNROLL
    for (; i < count-4; i += 4)
    {
        lhs[i+0] |= rhs[i+0];
        lhs[i+1] |= rhs[i+1];
        lhs[i+2] |= rhs[i+2];
        lhs[i+3] |= rhs[i+3];
    }
#endif

    for (; i < count; ++i)
        lhs[i] |= rhs[i];
}

void __stdcall SSEXor(uint64_t* lhs, uint64_t* rhs, int count)
{
    int i=0;

#ifdef UNROLL
    for (; i < count-4; i += 4)
    {
        lhs[i+0] ^= rhs[i+0];
        lhs[i+1] ^= rhs[i+1];
        lhs[i+2] ^= rhs[i+2];
        lhs[i+3] ^= rhs[i+3];
    }
#endif

    for (; i < count; ++i)
        lhs[i] ^= rhs[i];
}

void __stdcall SSENot(uint64_t* lhs, int count)
{
    int i=0;

#ifdef UNROLL
    for (; i < count-4; i += 4)
    {
        lhs[i+0] = ~lhs[i+0];
        lhs[i+1] = ~lhs[i+1];
        lhs[i+2] = ~lhs[i+2];
        lhs[i+3] = ~lhs[i+3];
    }
#endif

    for (; i < count; ++i)
        lhs[i] = ~lhs[i];
}

#endif